package com.xl.basic.utils;


import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.jwt.JWT;
import com.xl.basic.entity.Properties;
import com.xl.basic.entity.Result;
import com.xl.basic.entity.ResultEnums;
import com.xl.basic.entity.result.TokenResult;
import com.xl.basic.entity.UserInfo;
import com.xl.basic.service.LoginService;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpSession;
import jakarta.validation.constraints.NotBlank;
import lombok.NonNull;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.Date;

/**
 * 对登录做了基础的包装
 *
 * @author 钟勇
 * Create in 2023/8/24 16:47
 */
@Component
public class LoginUtils {

    private static LoginService loginService;

    @Autowired
    public LoginUtils(LoginService tmp) {
        loginService = tmp;
    }

    /**
     * 在全局系统中获取userInfo
     *
     * @return userInfo
     */
    public static Result<UserInfo> getUserInfo() {
        HttpSession session = ServletUtils.getSession();
        if (session == null) {
            return ResultEnums.LoginError.getResult("session未找到");
        }

        Properties.Auth auth = PropUtils.getProp().getAuth();
        UserInfo userInfo = (UserInfo) session.getAttribute(auth.getSessionName());

        String token = getRequestToken();

        if (StrUtil.isBlank(token) && userInfo == null) {
            return ResultEnums.LoginError.getResult("用户未登录");
        }

        String tokenUserId = null;
        if (StrUtil.isNotBlank(token)) {
            Result<String> tokenRes = decryptionToken(token);
            if (tokenRes.isErr()) {
                return ResultEnums.LoginError.getResult(tokenRes.getMessage());
            }
            tokenUserId = tokenRes.getResult();
        }

        //如果session中的登录信息和token中的userId一致的时候，自己返回
        if (userInfo != null && (StrUtil.isBlank(tokenUserId) || userInfo.getUserId().equals(tokenUserId))) {
            return ResultEnums.Success.getDataResult(userInfo);
        }

        //重新获取用户信息并写入系统
        if (tokenUserId == null || StrUtil.isBlank(tokenUserId)) {
            return ResultEnums.LoginError.getResult("用户未登录");
        }

        Result<UserInfo> userInfoRes = getUserInfo(tokenUserId);
        if (userInfoRes.isErr()) {
            return ResultEnums.LoginError.getResult(userInfoRes.getMessage());
        }

        return userInfoRes;
    }

    /**
     * 获取用户信息，通过新传入的userId
     *
     * @param userId user
     * @return 新的用户id
     */
    public static Result<UserInfo> getUserInfo(@NonNull String userId) {
        if (StrUtil.isBlank(userId)) {
            return ResultEnums.ParamError.getResult("用户ID不能为空");
        }

        Result<UserInfo> userInfoRes = loginService.getUserInfo(userId);
        if (userInfoRes.isSuc()) {
            //将新的用户信息保存
            setUserInfo(userInfoRes.getResult());
        }

        return userInfoRes;
    }

    /**
     * 将用户信息写入系统
     *
     * @param userInfo 用户信息
     */
    public static <T extends UserInfo> void setUserInfo(T userInfo) {
        HttpSession session = ServletUtils.getSession();
        if (session == null) {
            return;
        }

        Properties.Auth auth = PropUtils.getProp().getAuth();
        session.setAttribute(auth.getSessionName(), userInfo);
    }

    /**
     * 清除系统中的用户信息
     */
    public static void clearUserInfo() {
        HttpSession session = ServletUtils.getSession();
        if (session == null) {
            return;
        }

        Properties.Auth auth = PropUtils.getProp().getAuth();
        session.setAttribute(auth.getSessionName(), null);

    }

    /**
     * 在请求参数中获取token
     *
     * @return token
     */
    public static String getRequestToken() {
        HttpServletRequest request = ServletUtils.getRequest();

        Properties.Auth auth = PropUtils.getProp().getAuth();
        String authorization = request.getHeader("Authorization");
        if (StrUtil.isNotBlank(authorization) && authorization.startsWith(auth.getTokenPrefix())) {
            return authorization.substring(auth.getTokenPrefix().length());
        }

        return request.getParameter("token");
    }

    /**
     * 生成token
     *
     * @param userId userId
     * @return token
     */
    public static TokenResult generateToken(@NonNull String userId) {
        Properties.Auth auth = PropUtils.getProp().getAuth();
        LocalDateTime expires = LocalDateTime.now().plusSeconds(auth.getTokenExpire());

        String token = JWT.create()
                .setExpiresAt(Date.from(expires.atZone(ZoneId.systemDefault()).toInstant()))
                .setKey(auth.getTokenSecret().getBytes())
                .setPayload("userId", userId)
                .sign();


        return new TokenResult(token, expires);
    }


    public static Result<String> decryptionToken(@NonNull String token) {
        Properties.Auth auth = PropUtils.getProp().getAuth();
        JWT jwt = JWT.of(token).setKey(auth.getTokenSecret().getBytes());
        if (!jwt.verify()) {
            return ResultEnums.BusinessError.getResult("token验证不通过");
        }
        if (!jwt.validate(System.currentTimeMillis())) {
            return ResultEnums.BusinessError.getResult("token已失效");
        }

        String userId = (String) jwt.getPayload("userId");
        if (StrUtil.isBlank(userId)) {
            return ResultEnums.BusinessError.getResult("无效的荷载");
        }

        return ResultEnums.Success.getDataResult(userId);
    }

    /**
     * 登录密码加密
     *
     * @param password 密码
     * @return 加密后的密码
     */
    public static String encryptPassword(@NotBlank String password) {
        Properties.Auth auth = PropUtils.getProp().getAuth();
        return SecureUtil.md5(SecureUtil.md5(password) + auth.getPasswordSalt());
    }
}
